import java.io.BufferedOutputStream;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.util.Hashtable;
import java.util.Queue;
import java.util.concurrent.ArrayBlockingQueue;


public class IMHT implements Runnable{
	//attributes
	MyServerSocket mySrvSkt;
	DatagramSocket socket;
	ArrayBlockingQueue<DatagramPacket> queue;
	Thread inThread;
	int portID;
	Hashtable<String, ArrayBlockingQueue<DatagramPacket>> socketTable;
	
	//constructors
	public IMHT (){			
	}
	
	public IMHT(Queue queue, DatagramSocket socket){
		//instantiates a thread
		this.queue = (ArrayBlockingQueue) queue;
		this.socket = socket;
		
		inThread = new Thread(this);
		inThread.start();
	}
	
	@SuppressWarnings({ "rawtypes", "unchecked" }) 
	public IMHT(Queue queue, DatagramSocket socket, int portID){
		//instantiates a thread
		this.queue = (ArrayBlockingQueue<DatagramPacket>) queue;
		this.socket = socket;
		this.portID = portID;
		
		inThread = new Thread(this);
		inThread.start();
	}
	//methods	
	

			//accessors
	@SuppressWarnings("rawtypes")
	public Queue getQueue(){
		return queue;
	}
			//mutators

	@SuppressWarnings("unchecked")
	@Override
	public void run() {
		
		DatagramPacket inPkt;
		ObjectInputStream in;
		byte[] rcvPkt;
		boolean placed = false;
		
		while (true){
			//get packet
			rcvPkt = new byte[5000];
			//handle packet
			try{
				//set up object to place received packet
				inPkt = new DatagramPacket(rcvPkt, rcvPkt.length);
				//receive packet
				socket.receive(inPkt);	
				//convert Datagram Packet into readable object
				in = new ObjectInputStream(new ByteArrayInputStream(rcvPkt));
				Packet pkt = (Packet)in.readObject();
				in.close();
				
				//get packet's method request
				int type = pkt.getJob();
				
				//check for connection request or server request
				if (type == 0)
				{
					if(socketTable.size() < mySrvSkt.getConnLimit())
					{	if(mySrvSkt.getStatusFlag())
							queue.put(inPkt);
						
					
					mySrvSkt.setStatusFlag(false);	
					}
					else
					{
						Packet p = new Packet();
						p.setJob(type);
						send(p, inPkt.getAddress(), inPkt.getPort());
					}
				
				}
				// TODO  define close in job types
				else if (type == 4)
				{
					socketTable.remove((String)(inPkt.getAddress()+":"+inPkt.getPort()));
					send (pkt, inPkt.getAddress(), inPkt.getPort());
				}
				else{
					ArrayBlockingQueue <DatagramPacket> openQueue;
					openQueue = socketTable.get((String)(inPkt.getAddress()+":"+inPkt.getPort()));
					openQueue.put(inPkt);
				}
			}
		 catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		 }
				
			
		}//
		
	}

	public void send(Packet pkt, InetAddress address, int port){
		byte[] sendPkt;
		DatagramPacket packet;
		ByteArrayOutputStream b;
		ObjectOutputStream o;
		
		try{
			b = new ByteArrayOutputStream(5000);
			o = new ObjectOutputStream(new BufferedOutputStream(b));
			o.flush();
			o.writeObject(pkt);
			o.flush();
			sendPkt = b.toByteArray();
			packet = new DatagramPacket(sendPkt, sendPkt.length, address, port);
			mySrvSkt.getOutputBuffer().put(packet);
			
		}catch(Exception e){
			e.getStackTrace();
		}
	}
}
